﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HSDc.RingleFlow.Api.Kernel;
using HSDc.RingleFlow.Kernel.DBPersit.PersitEnum;
using System.IO;

namespace HSDc.RingleFlow.Kernel.DBPersit.Dao
{
    internal class RuntimeServiceDao
    {
        internal void NewProcessInstance(ProcessInstance instance)
        {
            // New Process Instance
            RingleFlowEntities entity = Util.ConnectionFactory.GetInstance().GetEntities();
            // Save Process Instance
            int instanceId = SaveProcessInstance(entity, instance, null);
            // Save Activity Instance
            AddActvitiyInstance(entity, instanceId, instance.GetCurrentTasks());
            // Save Variable
            AddInstanceVariable(entity, ((IProcessInstance)instance).Id, VariableType.ProcessInstance, ((IProcessInstance)instance).GetVaraibles());
            // Commit
            entity.SaveChanges(System.Data.Objects.SaveOptions.AcceptAllChangesAfterSave);
        }

        internal List<InstanceVariableDto> RetrieveVariables(string instanceId)
        {
            RingleFlowEntities entity = Util.ConnectionFactory.GetInstance().GetEntities();
            var variables = from var in entity.InstanceVariableDto
                            where ((var.Instance_GUID.ToLower() == instanceId.ToLower()) &&
                                       (var.Variable_Type == (int)VariableType.ProcessInstance))
                            select var;
            return variables.ToList();
        }

        internal InstanceVariableDto RetrieveVariables(string instanceId, string variableName)
        {
            RingleFlowEntities entity = Util.ConnectionFactory.GetInstance().GetEntities();
            var variable = (from var in entity.InstanceVariableDto
                            where ((var.Instance_GUID.ToLower() == instanceId.ToLower()) &&
                                       (var.Variable_Type == (int)VariableType.ProcessInstance)&&
                                       (var.Variable_Name == variableName))
                            select var).SingleOrDefault();
            return variable;
        }

        internal ProcessInstanceDto RetrieveProcessInstance(string instanceId)
        {
            RingleFlowEntities entity = Util.ConnectionFactory.GetInstance().GetEntities();
            var instance = (from pi in entity.ProcessInstanceDto
                            where (pi.ProcessInstance_GUID.ToLower() == instanceId.ToLower())
                            select pi).SingleOrDefault();
            return instance;
        }

        internal List<ActivityInstanceDto> RetrieveActivityInstance(int instanceId, TaskState state)
        {
            RingleFlowEntities entity = Util.ConnectionFactory.GetInstance().GetEntities();
            var actInstance = from ai in entity.ActivityInstanceDto
                              where ((ai.ActivityInstance_Belong_ProcessInstance==instanceId) &&
                                         (ai.ActivityInstance_Status==(int)state))
                              select ai;
            return actInstance.ToList();
        }

        internal string RetrieveActivityId(int id)
        {
            RingleFlowEntities entity = Util.ConnectionFactory.GetInstance().GetEntities();
            var guid = (from ac in entity.ProcessActivityDto
                        where (ac.Activity_ID == id)
                        select ac.Activity_GUID).SingleOrDefault();
            return guid;
        }

        internal string RetrieveProcessDefinitionId(int id)
        {
            RingleFlowEntities entity = Util.ConnectionFactory.GetInstance().GetEntities();
            var guid = (from pd in entity.ProcessDefinitionDto
                        where (pd.Definition_ID == id)
                        select pd.Definition_GUID).SingleOrDefault();
            return guid;
        }

        internal int RetrieveProcessInstanceFromHistory(string guid)
        {
            RingleFlowEntities entity = Util.ConnectionFactory.GetInstance().GetEntities();
            var pihs = from pih in entity.ProcessInstanceHistoryDto
                       where (pih.ProcessInstance_GUID == guid)
                       select pih;
            return pihs.ToList().Count;
        }

        #region Save Process Instance
        private int SaveProcessInstance(RingleFlowEntities entity, ProcessInstance instance, ProcessInstanceDto dto)
        {
            if (dto == null)
            {
                dto = ProcessInstanceDto.CreateProcessInstanceDto(0, ((IProcessInstance)instance).Id, DateTime.Now,
                    RetrieveDefinitionId(entity, ((IProcessInstance)instance).Definition.Id));
                entity.AddToProcessInstanceDto(dto);
            }
            FillProcessInstanceDtoValue(instance, ref dto);
            return dto.ProcessInstance_ID;
        }

        internal static void FillProcessInstanceDtoValue(ProcessInstance instance, ref ProcessInstanceDto dto)
        {
            dto.ProcessInstance_Status = (short)((IProcessInstance)instance).CurrentState;
            if (((IProcessInstance)instance).CurrentState == ExecutionState.Ended)
                dto.ProcessInstance_End = DateTime.Now;
        }

        private int RetrieveDefinitionId(RingleFlowEntities entity, string definitionId)
        {
            var definition = from pd in entity.ProcessDefinitionDto
                             where (pd.Definition_GUID.ToLower() == definitionId.ToLower())
                             select pd.Definition_ID;
            return definition.First();
        }
        #endregion

        #region Save Activity Instance
        private void AddActvitiyInstance(RingleFlowEntities entity, int instanceId, List<ActivityInstance> newActivity)
        {
            string acId;
            foreach (ActivityInstance ai in newActivity)
            {
                acId = Guid.NewGuid().ToString();
                ActivityInstanceDto dto = ActivityInstanceDto.CreateActivityInstanceDto(0, acId, DateTime.Now, instanceId, 
                    RetrieveActivityDefinitionId(entity, ai.ActivityDefinitionId));
                entity.AddToActivityInstanceDto(dto);
                FillActivityInstanceDtoValue(ai, ref dto);
            }
        }

        private int RetrieveActivityDefinitionId(RingleFlowEntities entity, string definitionId)
        {
            var activity = from pad in entity.ProcessActivityDto
                             where (pad.Activity_GUID.ToLower() == definitionId.ToLower())
                             select pad.Activity_ID;
            return activity.First();
        }

        private void FillActivityInstanceDtoValue(ActivityInstance instance, ref ActivityInstanceDto dto)
        {
            dto.ActivityInstance_Status = (int)((ITask)instance).CurrentState;
            dto.ActivityInstance_Assignee = ((ITask)instance).Assignee;
            if (((ITask)instance).CurrentState == TaskState.Completed)
                dto.ActivityInstance_End = DateTime.Now;
        }
        #endregion

        #region Save Variable
        private void AddInstanceVariable(RingleFlowEntities entity, string instanceId, VariableType type, Dictionary<string, object> variables)
        {
            object value;
            foreach (string key in variables.Keys)
            {
                InstanceVariableDto dto = InstanceVariableDto.CreateInstanceVariableDto(0, key, instanceId);
                entity.AddToInstanceVariableDto(dto);
                variables.TryGetValue(key, out value);
                FillInstanceVariableDtoValue(instanceId, type, value.ToString(), ref dto);
            }
        }

        private void FillInstanceVariableDtoValue(string instanceId, VariableType type, string value, ref InstanceVariableDto dto)
        {
            dto.Instance_GUID = instanceId;
            dto.Variable_Type = (int)type;
            dto.Variable_Value = value;
        }

        internal static void SaveProcessInstanceVariables(Dictionary<string, object> variables, string piId, RingleFlowEntities entity)
        {
            object value = null;
            InstanceVariableDto iv = null;
            foreach (string key in variables.Keys)
            {
                variables.TryGetValue(key, out value);
                var variable = from varDto in entity.InstanceVariableDto
                                where ((varDto.Variable_Name == key) &&
                                           (varDto.Instance_GUID == piId))
                                select varDto;
                if (variable.ToList().Count == 0)
                    iv = CreateNewVariableDto(piId, entity, key, iv);
                else
                    iv = variable.First();

                FillInstanceVariableDtoValue(value, iv);
            }
        }

        private static void FillInstanceVariableDtoValue(object value, InstanceVariableDto variable)
        {
            variable.Variable_Value = value.ToString();
            variable.Variable_Type = (int)VariableType.ProcessInstance;
        }

        private static InstanceVariableDto CreateNewVariableDto(string piId, RingleFlowEntities entity, string key, InstanceVariableDto variable)
        {
            variable = InstanceVariableDto.CreateInstanceVariableDto(0, key, piId);
            entity.AddToInstanceVariableDto(variable);
            return variable;
        }
        #endregion
    }
}
